#pragma once
#include <list>
#include <map>
#include "TCharSupport.h"
#include "EpilToken.h"
#include "EpilLexLocation.h"


class EpilScanner
{
public:

	EpilScanner();
	~EpilScanner();

	void								ScanFile( const tstring & filename );
	void								ScanString( const tstring & str );
	const list<EpilToken> &				GetTokens() const									{ return m_tokens; }

private:

	enum EpilScanState
	{
		ESS_Normal,
		ESS_InSingleQuoteString,
		ESS_InDoubleQuoteString,
		ESS_InSingleQuoteEscapeSequence,
		ESS_InDoubleQuoteEscapeSequence,
		ESS_InIdentifier,
		ESS_InDecimal,
		ESS_InOctal,
		ESS_InHexadecimal,
		ESS_InDouble,
		ESS_InLineComment,
		ESS_InBlockComment,
		ESS_InExponent,

		ESS_EnterDouble,
		ESS_EnterScientific,
		ESS_EnterExponent,
		ESS_OneZeroPassed,
		ESS_OneEqualPassed,							// =
		ESS_OneUnequalPassed,						// ~
		ESS_OneLessPassed,							// <
		ESS_OneGreaterPassed,						// >
		ESS_OneCloseBracketPassed,					// ]		Used by block comment.
		ESS_OneMinusPassed,							// -
		ESS_HalfBlockCommentPassed,					// --[
		ESS_OneDotPassed,							// .
		ESS_TwoDotPassed,							// ..
		ESS_OneEscapeDigitPassedInSingle,			// \X
		ESS_TwoEscapeDigitPassedInSingle,			// \XX
		ESS_OneEscapeDigitPassedInDouble,			// \X
		ESS_TwoEscapeDigitPassedInDouble,			// \XX

		ESS_MAXCOUNT,
		ESS_InvalidState
	};

	enum EpilScanCharType
	{
		ESCT_Null,
		ESCT_LineEnder,
		ESCT_AlphaOrUnderscore,
		ESCT_Digit,
		ESCT_TabOrSpace,

		ESCT_OpenParenthese,		// (
		ESCT_CloseParenthese,		// )
		ESCT_OpenBracket,			// [
		ESCT_CloseBracket,			// ]
		ESCT_OpenBrace,				// {
		ESCT_CloseBrace,			// }

		ESCT_Add,					// +
		ESCT_Minus,					// -
		ESCT_Star,					// *
		ESCT_Slash,					// /
		ESCT_Backslash,				// '\'
		ESCT_Power,					// ^
		ESCT_Percent,				// %
		ESCT_Dot,					// .
		ESCT_Less,					// <
		ESCT_Greater,				// >
		ESCT_Equal,					// =
		ESCT_Unequal,				// ~
		ESCT_Sharp,					// #
		ESCT_Colon,					// :
		ESCT_Semicolon,				// ;
		ESCT_SingleQuote,			// '
		ESCT_DoubleQuote,			// "
		ESCT_Comma,					// ,
		
		ESCT_Other,
		ESCT_MAXCOUNT
	};

	typedef EpilScanState (EpilScanner::*ScanProcessor)( tchar ch );

	void								Scan( tistream & program );
	static EpilScanCharType				GetScanCharType( tchar ch );
	void								AddToken( const EpilToken & token );
	void								AppendCharToCurrentString( tchar ch );
	void								ClearCurrentString();
	EpilLexLocation						GetCurrentCharLexLocation() const;
	EpilLexLocation						GetStringBeginLexLocation() const;
	void								AddIdentifierOrKeyword();

	list<EpilToken>						m_tokens;
	ScanProcessor						m_processors[ESS_MAXCOUNT];
	int									m_stringBeginLine;
	int									m_stringBeginColumn;
	int									m_curCharLine;
	int									m_curCharColumn;
	tstring								m_curString;
	tstring								m_curEscapeStr;
	map<tstring, EpilTokenType>			m_keywordMap;

private:

	EpilScanState						ProcessState_Normal( tchar ch );
	EpilScanState						ProcessState_InSingleQuoteString( tchar ch );
	EpilScanState						ProcessState_InDoubleQuoteString( tchar ch );
	EpilScanState						ProcessState_InSingleQuoteEscapeSequence( tchar ch );
	EpilScanState						ProcessState_InDoubleQuoteEscapeSequence( tchar ch );
	EpilScanState						ProcessState_InDecimal( tchar ch );
	EpilScanState						ProcessState_InOctal( tchar ch );
	EpilScanState						ProcessState_InHexadecimal( tchar ch );
	EpilScanState						ProcessState_InDouble( tchar ch );
	EpilScanState						ProcessState_InIdentifier( tchar ch );
	EpilScanState						ProcessState_InExponent( tchar ch );
	EpilScanState						ProcessState_InLineComment( tchar ch );
	EpilScanState						ProcessState_InBlockComment( tchar ch );

	EpilScanState						ProcessState_EnterDouble( tchar ch );
	EpilScanState						ProcessState_EnterScientific( tchar ch );
	EpilScanState						ProcessState_EnterExponent( tchar ch );
	EpilScanState						ProcessState_OneZeroPassed( tchar ch );
	EpilScanState						ProcessState_OneEqualPassed( tchar ch );
	EpilScanState						ProcessState_OneUnequalPassed( tchar ch );
	EpilScanState						ProcessState_OneLessPassed( tchar ch );
	EpilScanState						ProcessState_OneGreaterPassed( tchar ch );
	EpilScanState						ProcessState_OneCloseBracketPassed( tchar ch );
	EpilScanState						ProcessState_OneMinusPassed( tchar ch );
	EpilScanState						ProcessState_HalfBlockCommentPassed( tchar ch );
	EpilScanState						ProcessState_OneDotPassed( tchar ch );
	EpilScanState						ProcessState_TwoDotPassed( tchar ch );
	EpilScanState						ProcessState_OneEscapeDigitPassedInSingle( tchar ch );
	EpilScanState						ProcessState_OneEscapeDigitPassedInDouble( tchar ch );
	EpilScanState						ProcessState_TwoEscapeDigitPassedInSingle( tchar ch );
	EpilScanState						ProcessState_TwoEscapeDigitPassedInDouble( tchar ch );
};
